package edu.gmu.middleware;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

/**
 * SinkFactory provides utility methods to create various Sink objects.
 * 
 * @author  James H. Pope
 * @version $Revision:$ $Date:$
 */
public class SinkFactory
{
    
    public static final int DEFAULT_READ_BUFFER_SIZE = 1048576;
    
    //------------------------------------------------------------------------//
    // TCP Factory methods
    //------------------------------------------------------------------------//
    
    
    
    /**
     * Creates a Sink using the specified values.  Sink is
     * returned in a non-started state (you need to call open before sending
     * or receiving data).
     *
     * @param String remote address
     * @param int remote port
     * @return a newly created Sink
     */ 
    public static Sink makeTCPSink(  String remoteAddress,
                                     int remotePort ) throws IOException
    {
        SocketAddress remoteSocketAddress = new InetSocketAddress(  remoteAddress,
                                                                    remotePort );
        Sink sink = new TCPStreamSink( remoteSocketAddress, DEFAULT_READ_BUFFER_SIZE );
        
        return sink;
    }
    
    /**
     * Creates a Sink using the specified values.  Sink is
     * returned in a non-started state (you need to call open before sending
     * or receiving data).
     *
     * @param clientSocket
     * @param readBufferSize
     * @return a newly created Sink
     */ 
    public static Sink makeTCPSink( Socket clientSocket, int readBufferSize ) throws IOException
    {
        Sink sink = new TCPStreamSink( clientSocket, readBufferSize );
        return sink;
    }
    
    /**
     * Creates a Sink using the specified values.  This call will block while
     * carrying out exchanges of server handshake protocol messages.
     *
     * @param clientSocket
     * @return a newly created Sink
     */ 
//    public static Sink serverHandshake( Socket clientSocket ) throws IOException
//    {
//        Sink sink = new TCPStreamSink( clientSocket );
//        sink.connect();
//        
//        sink.addSinkListener(new SinkListener()
//        {
//            public void packetReceived( SinkEvent event )
//            {
//                try
//                {
//                    ByteBuffer in = event.getIn();
//                    Header header = new Header();
//                    header.unmarshal(in);
//                    
//                    if( header.getMessageId() == HandshakeProtocol.CONNECT )
//                    {
//                        Message message = unmarshaler.unmarshal(in);
//                        Connect connect = (Connect)message;
//                        
//                        EchoResponse response = new EchoResponse();
//                        response.count   = request.count++;
//                        response.missive = request.missive;
//
//                        marshaler.marshal(response, out);
//                        sink.write(out);
//                    }
//                }
//                catch (IOException ex)
//                {
//                    ex.printStackTrace();
//                }
//            }
//        }
//    );
//        
//        return sink;
//    }
    
    //------------------------------------------------------------------------//
    // UDP Factory Methods
    //------------------------------------------------------------------------//
    /**
     * Creates a Sink using the specified values.  Sink is
     * returned in a non-started state (you need to call opendat before sending
     * or receiving data).
     *
     * @param String remote address
     * @param int remote port
     * @param int local port
     *
     * @return a newly created Sink
     */ 
    public static Sink makeUDPClient(  String remoteAddress,
                                       int remotePort,
                                       int localPort) throws IOException
    {
        SocketAddress remoteSocketAddress = new InetSocketAddress(  remoteAddress,
                                                                    remotePort );
        
        /*
         * Use the "wildcard" as the local address.  If we used the localhost
         * the server would, for example, get a remote address of
         * 192.168.1.101:5001 however the client would be listening
         * on 127.0.0.1 - it won't be resolved the way you think it should.
         * The wildcard is more so used for UDP Servers which the Client
         * really becomes - so this is correct.
         *
         * One drawback is if we had
         * multiple Network Cards with multiple addresses and we only 
         * wanted to receive data from one of the NetworkInterfaces - the
         * wildcard approach will receive data from all of them.
         */
        //SocketAddress localAddress =
        //   new InetSocketAddress(InetAddress.getLocalHost(), localPort);
        SocketAddress localAddress = new InetSocketAddress( localPort );
        
        //Sink sink = new UDPStreamSink( remoteSocketAddress, localAddress );
        Sink sink = new UDPChannelSink( remoteSocketAddress, localAddress );
        
        return sink;
    }
    
    /**
     * Creates a Sink using the specified values.  Sink is
     * returned in a non-started state (you need to call opendat before sending
     * or receiving data).
     * @param remotePort
     * @param localPort
     * @return a newly created sink
     */ 
    public static Sink makeUDPServer(  int remotePort,
                                       int localPort )
         throws IOException
    {
        //DatagramSocket datagramSocket = new DatagramSocket( localPort );
        
        SocketAddress remoteAddress = new InetSocketAddress( remotePort );
        
        SocketAddress localAddress = new InetSocketAddress( localPort );
        
        Sink sink = new UDPChannelSink( remoteAddress, localAddress );
        
        return sink;
    }
    

}
